package br.ufc.great.dtx.tm.deadlockdetector;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Vector;

import br.ufc.great.Graph;
import br.ufc.great.dtx.server.DTXOutput;
import br.ufc.great.dtx.server.config.Config;
import br.ufc.great.dtx.server.config.DTXSite;
import br.ufc.great.dtx.tm.sch.Message;
import br.ufc.great.dtx.tm.sch.Scheduler;

/**
 * 
 * @author Leonardo Oliveira Moreira
 *
 * Processo inicializado periodicamente para verificar a ocorrência de deadlock distribuído
 */
public class DeadLockDaemon extends Thread {

	private DTXOutput output;
	private Scheduler scheduler;
	private Graph result;
	private int responses;
	private int requests;
	private Vector<DTXSite> sites;
	
	public DeadLockDaemon(DTXOutput output, Scheduler scheduler) {
		this.output = output;
		this.scheduler = scheduler;
		sites = Config.getDTXAllSites();
		setPriority(MIN_PRIORITY);		
	}
	
	public void run() {
		//output.print(getClass().getSimpleName(), "Distributed Deadlock Detector is running", DTXOutput.MESSAGE);
		while (isAlive()) {
			try {
				//output.print(getClass().getSimpleName(), "Distributed Deadlock Detector is sleeping", DTXOutput.TEST);
				sleep(Config.DTX_SERVER_DEADLOCK_TIME);
			}
			catch (InterruptedException e) {
				output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.ERROR);
			}
			//output.print(getClass().getSimpleName(), "Distributed Deadlock Detector in process", DTXOutput.TEST);
			result = new Graph();
			responses = 0;
			requests = 0;
			WFGMessage wfgMessage = new WFGMessage();
			for (int i = 0; i < sites.size(); i++) {
				try {
					//output.print(getClass().getSimpleName(), "Sending WFG request for site " + sites.elementAt(i).getHost() + ":" + sites.elementAt(i).getPort(), DTXOutput.TEST);
					DeadLockDaemonThread thread = new DeadLockDaemonThread(output, this, sites.elementAt(i), wfgMessage);
					thread.start();
				} catch (UnknownHostException e) {
					output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.ERROR);
				} catch (IOException e) {
					output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.ERROR);
				}
			}
			try {
				synchronized (this) {
					while (responses < requests) {
						//output.print(getClass().getSimpleName(), "Waiting for responses...", DTXOutput.TEST);
						wait();
					}
				}
				//output.print(getClass().getSimpleName(), "All responses arrived", DTXOutput.TEST);
				Vector<String> vertices = result.getVertices();
				for (int i = 0; i < vertices.size(); i++) {
					String transactionId = vertices.elementAt(i);
					if (result.isCircle(transactionId)) {
						//output.print(getClass().getSimpleName(), "Distributed deadlock found, transaction " + transactionId + " will abort", DTXOutput.ERROR);
						for (int j = 0; j < sites.size(); j++) {
							DTXSite site = sites.elementAt(j);
							try {
								//output.print(getClass().getSimpleName(), "Distributed abort remote message was sent to " + site.getHost() + ":" + site.getPort(), DTXOutput.ERROR);
								Socket socket = new Socket(site.getHost(), site.getPort());
								ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
								Message message = new Message(transactionId, Message.DISTRIBUTED_ABORT);
								out.writeObject(message);
								out.flush();
								out.close();
								socket.close();
							}
							catch (IOException e) {
								output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.ERROR);
							}
						}

						
					}
				}
			}
			catch (InterruptedException e) {
				output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.ERROR);
			}
		}
	}
	
	public synchronized void addGraph(Graph graph) {
		if (graph.getData() != null)
			result.unionGraph(graph.getData());
		addResponse();
	}
	
	public synchronized void addRequest() {
		requests++;
	}
	
	public synchronized void addResponse() {
		responses++;
		notify();
	}
	
}
